home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Multimedia / Resource Library: Multimedia.iso / hypercrd / xcmds / rtf-redr.hqx / rtf.c < prev    next >
Text File  |  1992-12-19  |  15KB  |  636 lines

  1. /*
  2.  * This software is copyright 1992 by Robert Morris.
  3.  * You may freely redistribute this software as shareware
  4.  * if you do so in the same form as you got it. If you find
  5.  * this software useful, please send $12 to:
  6.  *   Robert Morris
  7.  *   P.O. Box 1044
  8.  *   Harvard Square Station
  9.  *   Cambridge, MA 02238
  10.  *   ecognome@aol.com
  11.  * If you incorporate any of this software in any kind of
  12.  * commercial product, please send $2 per copy distributed
  13.  * to the above address.
  14.  */
  15.  
  16. /*
  17.  * utilities to help parse RTF.
  18.  */
  19.  
  20. #include <stdlib.h>
  21. #include <ctype.h>
  22. #include <string.h>
  23. #include <HyperXCmd.h>
  24. #include "lists.h"
  25. #include "rtf.h"
  26.  
  27. int sprintf(char *, ...);
  28.  
  29. extern int goterror;
  30. void error(char *);
  31.  
  32. typedef struct state{
  33.     int font;
  34.     int face;
  35.     int size;
  36.     int dirty;    /* do we need a new TE style before next char? */
  37.     int isfonttable;    /* \fonttbl group */
  38.     int deffont;    /* default rtf font # */
  39.     int invisible;
  40.     struct state *last;
  41. } State;
  42. State *pushstate(State *old), *popstate(State *);
  43.  
  44. struct ftable{
  45.     int rtf;    /* rtf font # */
  46.     int mac;    /* mac font # */
  47. } **ftable;
  48.  
  49. int startstyle(MyStyleHandle sh, long start, int font, int face, int size);
  50. void readcontrol(struct list *, char *word, char *arg);
  51. void docontrol(struct list *inp, struct list *outp, State *state,
  52.                MyStyleHandle sh, char control[], char arg[]);
  53. void appchars(struct list *outp, char *s, State *state, MyStyleHandle sh);
  54.  
  55. /*
  56.  * txt should be a handle to RTF text.
  57.  * puts a (partial) style handle into *outsh, and a text handle into *outtxt.
  58.  * if outsh is zero, don't bother with styles.
  59.  * the text handle is null-terminated.
  60.  */
  61. void
  62. parsertf(Handle txt, MyStyleHandle *outsh, Handle *outtxt)
  63. {
  64.     struct list out, in;
  65.     register int c;
  66.     STElement *stp;
  67.     MyStyleHandle sh;
  68.     register State *state;
  69.     
  70.     if(outsh)
  71.         *outsh = 0;
  72.     *outtxt = 0;
  73.     out.h = 0;
  74.     state = 0;
  75.     sh = 0;
  76.     ftable = 0;
  77.     
  78.     ftable = (struct ftable **)NewHandle(0L);
  79.     if(ftable == 0){
  80.         error("error : out of memory for ftable");
  81.         goto out;
  82.     }
  83.  
  84.     if(outsh){
  85.         sh = NewMyStyleHandle();
  86.         if(sh == 0){
  87.             error("error : out of memory for sh");
  88.             goto out;
  89.         }
  90.     } else {
  91.         sh = 0;
  92.     }
  93.  
  94.     state = pushstate((State *) 0);
  95.     if(state == 0){
  96.         error("error : out of memory for state");
  97.         goto out;
  98.     }
  99.     
  100.     NewList(txt, &in);
  101.     NewList((char **) 0, &out);
  102.  
  103.     while((c = ReadListChar(&in)) != -1 && c != '\0' && goterror == 0){
  104.         if(c == '\\'){
  105.             char control[256], arg[256];
  106.             readcontrol(&in, control, arg);
  107.             docontrol(&in, &out, state, sh, control, arg);
  108.         } else if(c == '{'){
  109.             state = pushstate(state);
  110.             if(state == 0){
  111.                 error("error : out of memory for new state");
  112.                 goto out;
  113.             }
  114.         } else if(c == '}'){
  115.             state = popstate(state);
  116.             if(state == 0){
  117.                 error("error : unbalanced }");
  118.                 goto out;
  119.             }
  120.             state->dirty = 1;
  121.         } else if(state->isfonttable == 0 &&
  122.                   c != '\r' &&
  123.                   c != '\n' &&
  124.                   state->invisible == 0){
  125.             if(state->dirty){
  126.                 startstyle(sh, out.ptr, state->font, state->face, state->size);
  127.                 state->dirty = 0;
  128.             }
  129.             _AppendListChar(&out, c);
  130.         }
  131.     }
  132.         
  133.     /* add dummy style to mark the end */
  134.     startstyle(sh, out.ptr, 0, 0, 12);
  135.     
  136.     if(sh)
  137.         (*sh)->nRuns -= 1; /* nRuns doesn't count that last dummy run! */
  138.     
  139.     if(goterror)
  140.         goto out;
  141.     
  142.     TrimList(&out);
  143.     if(out.h == 0){
  144.         error("error : out of memory for output text");
  145.         goto out;
  146.     }
  147.         
  148.     *outtxt = out.h;
  149.     out.h = 0;
  150.     if(outsh){
  151.         *outsh = sh;
  152.         sh = 0;
  153.     }
  154.  
  155. out:
  156.     if(ftable)
  157.         DisposHandle((Handle) ftable);
  158.     if(sh)
  159.         DisposMyStyleHandle(sh);
  160.     if(out.h)
  161.         DisposHandle(out.h);
  162.     while(state)
  163.         state = popstate(state);
  164. }
  165.  
  166. int
  167. startstyle(sh, start, font, face, size)
  168. register MyStyleHandle sh;
  169. long start;
  170. int font, face, size;
  171. {
  172.     register int si;
  173.     int run, err;
  174.     register STPtr st;
  175.     STPtr st1;
  176.     long newlen;
  177.     
  178.     if(sh == 0)
  179.         return;
  180.     
  181.     /* is there an existing style like this? */
  182.     st = 0;
  183.     for(si = 0; si < (*sh)->nStyles; si++){
  184.         st = (*((*sh)->styleTab)) + si;
  185.         if(st->stFont == font && st->stFace == face && st->stSize == size)
  186.             break;
  187.     }
  188.     if(si >= (*sh)->nStyles)
  189.         st = 0;
  190.     
  191.     /* is the last style just like this one? */
  192.     if(st && (*sh)->nRuns > 0 &&
  193.        (*sh)->runs[(*sh)->nRuns-1].styleIndex == si)
  194.         return;
  195.     
  196.     if(st == 0){
  197.         (*sh)->nStyles += 1;
  198.         SetHandleSize((Handle)((*sh)->styleTab),
  199.                       (*sh)->nStyles * sizeof(STElement));
  200.         if((err = MemError()) != 0){
  201.             (*sh)->nStyles = 0;
  202.             SetHandleSize((Handle)((*sh)->styleTab), 0L);
  203.             error("error : out of memory for more styleTab");
  204.             return(err);
  205.         }
  206.         si = (*sh)->nStyles - 1;
  207.         st = (*((*sh)->styleTab)) + si;
  208.         st->stCount = 0;
  209.         st->stHeight = 12; /* ??? */
  210.         st->stAscent = 10; /* ??? */
  211.         st->stFont = font;
  212.         st->stFace = face;
  213.         st->stSize = size;
  214.         st->stColor.red = st->stColor.green = st->stColor.blue = 0;
  215.     }
  216.     
  217.     st->stCount += 1;
  218.     
  219.     if((*sh)->nRuns > 0 && (*sh)->runs[(*sh)->nRuns-1].startChar == start){
  220.         /* overwrite previous run, since it starts in the same place */
  221.         run = (*sh)->nRuns - 1;
  222.         st1 = (*((*sh)->styleTab)) + (*sh)->runs[run].styleIndex;
  223.         st1->stCount -= 1;
  224.     } else {
  225.         run = (*sh)->nRuns;
  226.         (*sh)->nRuns += 1;
  227.         newlen = (char *) &((*sh)->runs[(*sh)->nRuns]) - (char *) *sh;
  228.         SetHandleSize((Handle)sh, newlen);
  229.         if((err = MemError()) != 0){
  230.             (*sh)->nRuns -= 1;
  231.             error("error : out of memory for more runs");
  232.             return(err);
  233.         }
  234.     }
  235.     
  236.     (*sh)->runs[run].startChar = start;
  237.     (*sh)->runs[run].styleIndex = si;
  238.     
  239.     return(0);
  240. }
  241.  
  242. /*
  243.  * a '\\' has been read; read the control word and any argument.
  244.  * if the following delimiter is a space, consume it; otherwise
  245.  * leave it alone.
  246.  */
  247. void
  248. readcontrol(lp, word, arg)
  249. struct list *lp;
  250. char word[], arg[];    /* out parameters: the control word and argument */
  251. {
  252.     int i, c;
  253.     
  254.     word[0] = arg[0] = '\0';
  255.     
  256.     i = 0;
  257.     while(i < 254 && isalpha(c = ReadListChar(lp)))
  258.         word[i++] = c;
  259.     word[i] = '\0';
  260.     
  261.     if(i == 0){
  262.         switch(c){
  263.         case '\'':
  264.             /* \' followed by two hex digits */
  265.             word[0] = '\'';
  266.             word[1] = '\0';
  267.             arg[0] = ReadListChar(lp);
  268.             arg[1] = ReadListChar(lp);
  269.             arg[2] = '\0';
  270.             return;
  271.         default:
  272.             /* some one-character punctuation code */
  273.             word[0] = c;
  274.             word[1] = '\0';
  275.             return;
  276.         }
  277.     }
  278.     
  279.     if(isdigit(c) || c == '-'){
  280.         /* we have a parameter */
  281.         i = 0;
  282.         do{
  283.             arg[i++] = c;
  284.         } while(i < 254 && isdigit(c = ReadListChar(lp)));
  285.         arg[i] = '\0';
  286.     }
  287.     
  288.     if(c != ' ' && c != '\n' && c != '\r'){
  289.         /* give back the look-ahead character */
  290.         lp->ptr -= 1;
  291.     }
  292. }
  293.  
  294. State *
  295. pushstate(old)
  296. State *old;
  297. {
  298.     State *new;
  299.     
  300.     new = (State *) NewPtr(sizeof(State));
  301.     if(new == 0){
  302.         while(old){
  303.             new = old->last;
  304.             DisposPtr((Ptr)old);
  305.             old = new;
  306.         }
  307.         return(0);
  308.     }
  309.     
  310.     if(old){
  311.         *new = *old;
  312.     } else {
  313.         new->font = new->face = 0;
  314.         new->size = 12;
  315.         new->dirty = 1;
  316.         new->isfonttable = 0;
  317.         new->invisible = 0;
  318.         new->deffont = -1;
  319.     }
  320.     
  321.     new->last = old;
  322.     return(new);
  323. }
  324.  
  325. State *
  326. popstate(st)
  327. State *st;
  328. {
  329.     State *last;
  330.     
  331.     if(st){
  332.         last = st->last;
  333.         DisposPtr((Ptr)st);
  334.         return(last);
  335.     }
  336.     return(0);
  337. }
  338.  
  339. void
  340. docontrol(inp, outp, state, sh, control, arg)
  341. struct list *inp, *outp;
  342. State *state;
  343. MyStyleHandle sh;
  344. char control[], arg[];
  345. {
  346.     int c;
  347.     
  348.     if(strcmp(control, "fonttbl") == 0){
  349.         state->isfonttable = 1;
  350.         SetHandleSize((Handle)ftable, 0L);
  351.     } else if(strcmp(control, "f") == 0 && state->isfonttable){
  352.         int mac, rtf, i, nfonts;
  353.         char family[256], name[256];
  354.  
  355.         rtf = atoi(arg);
  356.         
  357.         c = ReadListChar(inp);
  358.         while(isspace(c))
  359.             c = ReadListChar(inp);
  360.         if(c == '\\'){
  361.             readcontrol(inp, family, name);    /* read the family */
  362.         } else {
  363.             inp->ptr -= 1;
  364.             family[0] = '\0';
  365.         }
  366.         
  367.         /* read the font name, up to a semicolon */
  368.         i = 0;
  369.         while(i < 254 && (c = ReadListChar(inp)) != -1 && c != 0 &&
  370.               c != ';' && c != '}' && c != '\\'){
  371.             if(i == 0 && isspace(c))
  372.                 continue;
  373.             name[i++] = c;
  374.         }
  375.         name[i] = '\0';
  376.         CtoPstr(name);
  377.         GetFNum((StringPtr)name, &mac);
  378.         nfonts = GetHandleSize((Handle) ftable) / sizeof(**ftable);
  379.         SetHandleSize((Handle) ftable, sizeof(**ftable) * (nfonts + 1));
  380.         if(MemError() == 0){
  381.             (*ftable)[nfonts].rtf = rtf;
  382.             (*ftable)[nfonts].mac = mac;
  383.         } else
  384.             error("error : out of memory for more ftable");
  385.     } else if(strcmp(control, "f") == 0){
  386.         state->font = findrtffont(atoi(arg));
  387.         state->dirty = 1;
  388.     } else if(strcmp(control, "deff") == 0){
  389.         state->deffont = atoi(arg);
  390.     } else if(strcmp(control, "plain") == 0){
  391.         state->font = findrtffont(state->deffont);
  392.         state->face = 0;
  393.         state->size = 12;    /* ??? */
  394.         state->dirty = 1;
  395.         state->invisible = 0;
  396.     } else if(strcmp(control, "b") == 0){
  397.         xface(state, bold, arg);
  398.     } else if(strcmp(control, "i") == 0){
  399.         xface(state, italic, arg);
  400.     } else if(strcmp(control, "outl") == 0){
  401.         xface(state, outline, arg);
  402.     } else if(strcmp(control, "shad") == 0){
  403.         xface(state, shadow, arg);
  404.     } else if(strcmp(control, "hcgroup") == 0){
  405.         xface(state, 0x80, arg);    /* HyperCard grouped text */
  406.     } else if(strcmp(control, "v") == 0){
  407.         if(arg[0] == '0')
  408.             state->invisible = 0;
  409.         else
  410.             state->invisible = 1;
  411.     } else if(strcmp(control, "fs") == 0){
  412.         state->size = atoi(arg) / 2;
  413.         state->dirty = 1;
  414.     } else if(strcmp(control, "ul") == 0 || strcmp(control, "ulw") == 0 ||
  415.               strcmp(control, "uld") == 0 || strcmp(control, "uldb") == 0){
  416.         xface(state, underline, arg);
  417.     } else if(strcmp(control, "ulnone") == 0){
  418.         state->face &= ~underline;
  419.         state->dirty = 1;
  420.     } else if(strcmp(control, "~") == 0){
  421.         appchars(outp, "\xCA", state, sh); /* option-space */
  422.     } else if(strcmp(control, "_") == 0){
  423.         appchars(outp, "-", state, sh);
  424.     } else if(strcmp(control, "bullet") == 0){
  425.         appchars(outp, "Ñ", state, sh); /* option-8 */
  426.     } else if(strcmp(control, "ldblquote") == 0){
  427.         appchars(outp, "╥", state, sh); /* option-[ */
  428.     } else if(strcmp(control, "rdblquote") == 0){
  429.         appchars(outp, "╙", state, sh); /* option-shift-[ */
  430.     } else if(strcmp(control, "endash") == 0){
  431.         appchars(outp, "╨", state, sh); /* option-- */
  432.     } else if(strcmp(control, "emdash") == 0){
  433.         appchars(outp, "╤", state, sh); /* option-shift-- */
  434.     } else if(strcmp(control, "lquote") == 0){
  435.         appchars(outp, "╘", state, sh); /* option-] */
  436.     } else if(strcmp(control, "rquote") == 0){
  437.         appchars(outp, "╒", state, sh); /* option-shift-] */
  438.     } else if(strcmp(control, "'") == 0){
  439.         char bf[2];
  440.         sscanf(arg, "%x", &c);
  441.         bf[0] = c;
  442.         bf[1] = '\0';
  443.         appchars(outp, bf, state, sh);
  444.     } else if(strcmp(control, "line") == 0){
  445.         appchars(outp, "\r", state, sh);
  446.     } else if(strcmp(control, "tab") == 0){
  447.         appchars(outp, "    ", state, sh);
  448.     } else if(strcmp(control, "colortbl") == 0 ||
  449.               strcmp(control, "pict") == 0 ||
  450.               strcmp(control, "footnote") == 0 ||
  451.               strcmp(control, "annotation") == 0 ||
  452.               strcmp(control, "header") == 0 ||
  453.               strcmp(control, "footer") == 0 ||
  454.               strcmp(control, "info") == 0 ||
  455.               strcmp(control, "field") == 0 ||
  456.               strcmp(control, "*") == 0){
  457.         flushgroup(inp);
  458.     } else if(strcmp(control, "stylesheet") == 0){
  459.         flushgroup(inp);    /* seems to be OK to ignore */
  460.     } else if(strcmp(control, "par") == 0){
  461.         appchars(outp, "\r", state, sh);
  462.     } else if(isalnum(control[0]) == 0){
  463.         appchars(outp, control, state, sh);
  464.     }
  465. }
  466.  
  467. void
  468. appchars(outp, s, state, sh)
  469. struct list *outp;
  470. char *s;
  471. State *state;
  472. MyStyleHandle sh;
  473. {
  474.     if(state->invisible == 0){
  475.         if(state->dirty){
  476.             startstyle(sh, outp->ptr, state->font, state->face, state->size);
  477.             state->dirty = 0;
  478.         }
  479.         AppendList(outp, s);
  480.     }
  481. }
  482.         
  483.  
  484. int
  485. findrtffont(rtf)
  486. {
  487.     int i, nfonts;
  488.     
  489.     nfonts = GetHandleSize((Handle) ftable) / sizeof(**ftable);
  490.     for(i = 0; i < nfonts; i++){
  491.         if((*ftable)[i].rtf == rtf){
  492.             return((*ftable)[i].mac);
  493.         }
  494.     }
  495.     return(0);
  496. }
  497.  
  498. xface(state, face, arg)
  499. State *state;
  500. int face;
  501. char arg[];
  502. {
  503.     if(arg[0] == '0')
  504.         state->face &= ~face;
  505.     else
  506.         state->face |= face;
  507.     state->dirty = 1;
  508. }
  509.  
  510. flushgroup(inp)
  511. struct list *inp;
  512. {
  513.     int c, depth;
  514.     
  515.     depth = 0;
  516.     while((c = ReadListChar(inp)) != -1 && c != 0){
  517.         if(c == '}'){
  518.             if(depth <= 0){
  519.                 inp->ptr -= 1; /* allow } to pop the state */
  520.                 break;
  521.             }
  522.             --depth;
  523.         } else if(c == '{'){
  524.             depth++;
  525.         }
  526.     }
  527. }
  528.  
  529. MyStyleHandle
  530. NewMyStyleHandle(void)
  531. {
  532.     MyStyleHandle sh;
  533.     
  534.     sh = (MyStyleHandle) NewHandle(sizeof(**sh));
  535.     if(sh == 0)
  536.         return(0);
  537.         
  538.     (*sh)->nRuns = 0;
  539.     (*sh)->nStyles = 0;
  540.     (*sh)->styleTab = (STHandle) NewHandle(0L);
  541.     if((*sh)->styleTab == 0){
  542.         DisposHandle(sh);
  543.         return(0);
  544.     }
  545.     return(sh);
  546. }
  547.  
  548. void
  549. DisposMyStyleHandle(MyStyleHandle sh)
  550. {
  551.     if(sh){
  552.         if((*sh)->styleTab)
  553.             DisposHandle((*sh)->styleTab);
  554.         (*sh)->styleTab = 0;
  555.         DisposHandle(sh);
  556.     }
  557. }
  558.  
  559. /*
  560.  * convert one of my style handles into a TextEdit TEStyleHandle.
  561.  * since my style handles can deal with > 32K of text, *start
  562.  * indicates where to start converting. this routine leaves the
  563.  * first unconverted character position in *start when it returns.
  564.  * it tries not to split lines. it modifies an existing style handle.
  565.  * max specifies how many characters can be put in a field.
  566.  * txt is the total txt (ie might be > 32K).
  567.  * remember that (*sh)->nRuns doesn't count that last dummy run.
  568.  */
  569. OSErr
  570. CvtMyStyleHandle(MyStyleHandle sh1, long *start, TEStyleHandle sh2, long max,
  571.                  Handle txt)
  572. {
  573.     STHandle st;
  574.     long firstrun, lastrun, run, txtlen, len, i;
  575.     OSErr err;
  576.     
  577.     txtlen = GetHandleSize(txt);
  578.     if(*start + max > txtlen)
  579.         max = txtlen - *start;
  580.         
  581.     /* shrink max soas to break at a return */
  582.     if(*start + max < txtlen){
  583.         for(i = max-1 ; i >= 0; --i)
  584.             if((*txt)[*start + i] == '\r')
  585.                 break;
  586.         if(i > 0)
  587.             max = i + 1;
  588.     }
  589.     
  590.     /* find the first relevant run */
  591.     for(run = 0; run < (*sh1)->nRuns; run++)
  592.         if((*sh1)->runs[run].startChar <= *start &&
  593.            (*sh1)->runs[run + 1].startChar > *start)
  594.             break;
  595.     firstrun = run;
  596.             
  597.     /* find the last relevant run: it must be within max of *start */
  598.     for(run = firstrun; run <= (*sh1)->nRuns; run++)
  599.         if((*sh1)->runs[run].startChar >= *start + max)
  600.             break;
  601.     lastrun = run - 1;
  602.     
  603.     /*
  604.      * allocate enough space in sh2's tables.
  605.      * also copies sh1's styleTab. this might be a bit
  606.      * wasteful if any are unused, but it means that
  607.      * the same indices can be used in sh2 as in sh1.
  608.      */
  609.     (*sh2)->nRuns = lastrun - firstrun + 1;
  610.     (*sh2)->nStyles = (*sh1)->nStyles;
  611.     st = (*sh1)->styleTab;
  612.     HandToHand(&st);
  613.     (*sh2)->styleTab = st;
  614.     SetHandleSize(sh2, 32L + ((*sh2)->nRuns + 1) * sizeof(StyleRun));
  615.     if((err = MemError()) != 0)
  616.         return(err);
  617.         
  618.     /* copy the relevant runs, adjusting the character pointers */
  619.     for(run = firstrun; run <= lastrun; run++){
  620.         (*sh2)->runs[run - firstrun].startChar = (*sh1)->runs[run].startChar - *start;
  621.         if((*sh2)->runs[run - firstrun].startChar < 0)
  622.             (*sh2)->runs[run - firstrun].startChar = 0;
  623.         (*sh2)->runs[run - firstrun].styleIndex = (*sh1)->runs[run].styleIndex;
  624.     }
  625.     
  626.     if(lastrun < (*sh1)->nRuns)
  627.         len = (*sh1)->runs[lastrun + 1].startChar - *start;
  628.     else
  629.         len = txtlen - *start;
  630.     if(len > max)
  631.         len = max;
  632.     (*sh2)->runs[(*sh2)->nRuns].startChar = len;
  633.     *start += len;
  634.     
  635.     return(0);
  636. }